function PDF_Interpolate(x, xmin, xmax, ymin, ymax) 
{
	return ((x - xmin) * (ymax - ymin) / (xmax - xmin)) + ymin;
}

function _GetBits32(bytes, bitpos, nbits) {
    var result = 0;
    for (var i = 0; i < nbits; i++) {
        var index = Math.floor((bitpos + i) / 8);
        if (bytes[index] & (1 << (7 - (bitpos + i) % 8)))
            result |= 1 << (nbits - i - 1);
    }
	return result;
}


function SampleEncodeInfo() {
    var encode_max = 0;
    var encode_min = 0;
	var sizes = 0;
}

function SampleDecodeInfo() {
    var decode_max = 0; 
    var decode_min = 0;
}

function PDSampledFunc() {
    this.m_EncodeInfo = [];
    this.m_DecodeInfo = [];
    this.m_nBitsPerSample = 0;
    this.m_SampleMax = 0;

    this.m_nInputs = 0;
    this.m_nOutputs = 0;
    this.m_Domains = [];
    this.m_Ranges = [];

    this.m_SampleBuf = null; //Uint8Array
  //  this.m_BitString = [];
    this.CountOutputs = function(){return this.m_nOutputs;};

}

PDSampledFunc.prototype.Init = function (Domains/*array*/, Ranges/*array*/, sizes, encodes, decodes, bitpersample, sample_buf/*Uint8Array*/, smpMax) {

    if (sizes == undefined || sizes == null) return false;
    this.m_nOutputs = 0;
    this.m_nInputs = 0;
    if (Domains != undefined && Domains !== null) {
        this.m_Domains = Domains;
        this.m_nInputs = Domains.length / 2;
    }

   
    if (Ranges != undefined && Ranges !== null) {
        this.m_Ranges = Ranges;
        this.m_nOutputs = Ranges.length / 2;
    }

    var old_outputs = this.m_nOutputs;
    // Type specific init
    var ret = this.vInit(sizes, encodes, decodes, bitpersample, sample_buf);
    if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_nOutputs > old_outputs) {
        //reset m_Ranges length and set zero to new element
        for (var i = 0; i < (this.m_nOutputs - old_outputs) * 2; i++)
            this.m_Ranges[old_outputs * 2 + i] = 0;
    }
    return ret;
};
/*
function _Byte2String(byte) {
    var a = new Array(8);
    var i = 0;
    while (i < 8) {
        if ((byte % 2) != 0) {
            byte--;
            byte = byte / 2;
            a[i++] = 1;
        }
        else {
            byte = byte / 2;
            a[i++] = 0;
        }
    }
    a.reverse();
    return a;
}
*/
PDSampledFunc.prototype.vInit = function (sizes, encodes, decodes, bitpersample, sample_buf/*Uint8Array*/) {
    if (sample_buf.length <= 0) return false;
    this.m_SampleBuf = sample_buf;
 /*   
    for (var i = 0; i < sample_buf.length; i++) {
        var a = _Byte2String(sample_buf[i]);
        var len = this.m_BitString.length;
        for (var j = 0; j < 8; j++)
            this.m_BitString[len + j] = a[j];
    }
    */
    this.m_nBitsPerSample = bitpersample;
    this.m_SampleMax = 0xffffffff >>> (32 - this.m_nBitsPerSample);

    var i = 0; var nTotalSamples = 1;
    for (i = 0; i < this.m_nInputs; i++) {
        var encode_info = new SampleEncodeInfo();
        encode_info.sizes = sizes[i];
        if (nTotalSamples > 0 && encode_info.sizes > (0xffffffff / nTotalSamples)) return false;

        nTotalSamples *= encode_info.sizes;

        if (encodes != undefined && encodes != null && encodes.length > 0) {
            encode_info.encode_min = encodes[i * 2];
            encode_info.encode_max = encodes[i * 2 + 1];
        }
        else {
            encode_info.encode_min = 0;
            if (encode_info.sizes === 1)
                encode_info.encode_max = 1;
            else
                encode_info.encode_max = encode_info.sizes - 1;
        }
        this.m_EncodeInfo[i] = encode_info;
    }

    if (nTotalSamples > 0 && this.m_nBitsPerSample > 0xffffffff / nTotalSamples) return false;
    nTotalSamples *= this.m_nBitsPerSample;
    if (nTotalSamples > 0 && this.m_nOutputs > 0xffffffff / nTotalSamples) return false;
    nTotalSamples *= this.m_nOutputs;

    for (i = 0; i < this.m_nOutputs; i++) {
        var decode_info = new SampleDecodeInfo();
        if (decodes != undefined && decodes != null && decodes.length > 0) {
            decode_info.decode_min = decodes[2 * i];
            decode_info.decode_max = decodes[2 * i + 1];
        } else {
            decode_info.decode_min = this.m_Ranges[i * 2];
            decode_info.decode_max = this.m_Ranges[i * 2 + 1];
        }
        this.m_DecodeInfo[i] = decode_info;
    }
    return true;
};

PDSampledFunc.prototype.Call = function (inputs/*array*/, ninputs, results/*array*/) {
    if (this.m_nInputs != ninputs) return [false, 0];
    nresults = this.m_nOutputs;

	// First, clip all inputs to fit the domain
	for (var i = 0; i < this.m_nInputs; i++) {
	    if (inputs[i] < this.m_Domains[i * 2])
	        inputs[i] = this.m_Domains[i * 2];
	    else if (inputs[i] > this.m_Domains[i * 2 + 1])
	        inputs[i] = this.m_Domains[i * 2] + 1;
	}

    // Then, call the function
	this.vCall(inputs, results);

	// Finally, clip all outputs if ranges specified
	if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length > 0) {
	    for (var i = 0; i < this.m_nOutputs; i++) {
	        if (results[i] < this.m_Ranges[i * 2])
	            results[i] = this.m_Ranges[i * 2];
			else if (results[i] > this.m_Ranges[i*2+1])
				results[i] = this.m_Ranges[i*2+1];
		}
	}
    return [true, nresults];//[/*bool Value for this function's result, nResults, */]
};
PDSampledFunc.prototype.vCall = function(inputs/*array*/,results/*array*/){
    var encoded_input_buf=[];
    //
    var int_buf=[];
    //var index = 0;
    //var block_size = index + this.m_nInputs;
    var pos = 0;
    for (var i = 0; i < this.m_nInputs; i++) {
        if(i === 0){
            int_buf[this.m_nInputs + i] = 1;
        }else{
            int_buf[this.m_nInputs + i] = int_buf[this.m_nInputs + i - 1] * this.m_EncodeInfo[i - 1].sizes;
        }

        // 1. Encode inputs
        encoded_input_buf[i] = PDF_Interpolate(inputs[i], this.m_Domains[i * 2], this.m_Domains[i * 2 + 1],
				this.m_EncodeInfo[i].encode_min, this.m_EncodeInfo[i].encode_max);
        // 2. Get integer part
		int_buf[i] = parseInt(encoded_input_buf[i]);

        // 3. Clipped into size of sample table
		if (int_buf[i] < 0)
			int_buf[i] = 0;
		else if (int_buf[i] > this.m_EncodeInfo[i].sizes - 1)
			int_buf[i] = this.m_EncodeInfo[i].sizes - 1;

		// Advance to the position
		pos += int_buf[i] * int_buf[this.m_nInputs+i];
    }

    // Now we got the position. Calculate bit position
	var bitpos = pos * this.m_nBitsPerSample * this.m_nOutputs;

	for (var j = 0; j < this.m_nOutputs; j++) {
	    // 1. Get sampled value at the nearest point
        var sample = _GetBits32(this.m_SampleBuf, bitpos+j*this.m_nBitsPerSample, this.m_nBitsPerSample);
		var encoded = sample;

        // 2. Now use interpolation to adjust the sample value for EACH input
		for (var i = 0; i < this.m_nInputs; i ++) {
			if (int_buf[i] == this.m_EncodeInfo[i].sizes-1) {
				// At last sample. Typically we don't need to adjust any more,
				// however we need to handle one special case: sample size is 1, then we should interpolate with 0
				// TESTDOC: Bug #12402 - TestFile.pdf 
				if (int_buf[i] == 0)
					encoded = encoded_input_buf[i] * sample;
			} else {
				// now we interpolate between index[i] and index[i]+1, which is separated by blocksize[i] samples
				var bitpos1 = bitpos + this.m_nBitsPerSample * this.m_nOutputs * int_buf[this.m_nInputs + i];
				var sample1 = _GetBits32(this.m_SampleBuf, bitpos1 + j * this.m_nBitsPerSample, this.m_nBitsPerSample);
				// apply the ratio for changes.
				// We need to convert the sample to float otherwise it might overflow as integers
				// TESTDOC: bug #1388
				encoded += (encoded_input_buf[i] - int_buf[i]) * (sample1 - sample);
			}
		}

		// 3. Decode the sample value
		results[j] = PDF_Interpolate(encoded, 0, this.m_SampleMax, 
				this.m_DecodeInfo[j].decode_min, this.m_DecodeInfo[j].decode_max);
    }
    return true;
};

/*
 *	ExpIntFunc
 */

 function PDExpIntFunc() {
    this.m_nInputs = 0;
    this.m_nOutputs = 0;
    this.m_Domains = [];
    this.m_Ranges = [];

    this.m_Exponent = 0;
    this.m_BeginValues = [];
    this.m_EndValues = [];
    this.m_nOrigOutputs = 0;
    this.CountOutputs = function(){return this.m_nOutputs;};

}

PDExpIntFunc.prototype.Init = function (Domains/*array*/,Ranges/*array*/, arr0, arr1, exponent) {
    this.m_nOutputs = 0;
    this.m_nInputs = 0;
    if (Domains != undefined && Domains !== null) {
        this.m_Domains = Domains;
        this.m_nInputs = Domains.length / 2;
    }

    if (Ranges != undefined && Ranges !== null) {
        this.m_Ranges = Ranges;
        this.m_nOutputs = Ranges.length / 2;
    }

    var old_outputs = this.m_nOutputs;
    // Type specific init
    //var ret = this.vInit(sizes, encodes, decodes, bitpersample, sample_buf);
	var ret = this.vInit(arr0, arr1, exponent); //Modeify by haipeng_yang
	if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length > 0 && this.m_nOutputs > old_outputs)
    {
     //reset m_Ranges length and set zero to new element
        for(var i=0; i<(this.m_nOutputs - old_outputs) * 2; i++)
           this.m_Ranges[old_outputs * 2 + i] = 0;
    }
    return ret;
};
PDExpIntFunc.prototype.vInit = function (arr0, arr1, exponent) {
    if (this.m_nOutputs == 0) {
		this.m_nOutputs = 1;
		if (arr0.length > 0) this.m_nOutputs = arr0.length;
	}

    for (var i = 0; i < this.m_nOutputs; i ++) {
		this.m_BeginValues[i] = arr0.length > 0 ? arr0[i] : 0.0;
		this.m_EndValues[i] = arr1.length > 0 ? arr1[i] : 1.0;
	}

	this.m_Exponent = exponent;

    this.m_nOrigOutputs = this.m_nOutputs;
	if (this.m_nOutputs && this.m_nInputs > 2147483647 / this.m_nOutputs) 
		return false;
	this.m_nOutputs *= this.m_nInputs; 

	return true;
};


PDExpIntFunc.prototype.Call = function (inputs/*array*/, ninputs, results/*array*/) {

    if (this.m_nInputs != ninputs) return [false, 0];
	nresults = this.m_nOutputs;

	// First, clip all inputs to fit the domain
	for (var i = 0; i < this.m_nInputs; i ++) {
		if (inputs[i] < this.m_Domains[i*2])
			inputs[i] = this.m_Domains[i*2];
		else if (inputs[i] > this.m_Domains[i*2+1])
			inputs[i] = this.m_Domains[i*2]+1;
	}

    // Then, call the function
	this.vCall(inputs, results);

	// Finally, clip all outputs if ranges specified
	if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length > 0) {
		for (var i = 0; i < this.m_nOutputs; i ++) {
			if (results[i] < this.m_Ranges[i*2])
				results[i] = this.m_Ranges[i*2];
			else if (results[i] > this.m_Ranges[i*2+1])
				results[i] = this.m_Ranges[i*2+1];
		}
	}

    return [true, nresults];//[/*bool Value for this function's result, nResults, */] 
};
PDExpIntFunc.prototype.vCall = function(inputs/*array*/,results/*array*/){
    for (var i = 0; i < this.m_nInputs; i ++)
		for (var j = 0; j < this.m_nOrigOutputs; j ++) {
			results[i*this.m_nOrigOutputs + j] = this.m_BeginValues[j] + Math.pow(inputs[i], this.m_Exponent) *
					(this.m_EndValues[j] - this.m_BeginValues[j]);
		}

	return true;
};

function _LoadAFunc(jsonDataForAFunc) {
    var tempObj = jsonDataForAFunc;
    var func = null;
    
    if (tempObj[0] === 0) {
        func = new PDSampledFunc();
        var domain = stringArr2NumArr(tempObj[2]);
        var range = stringArr2NumArr(tempObj[3]);
        var size = stringArr2NumArr(tempObj[1]);
        var encode = stringArr2NumArr(tempObj[6]);
        var decode = stringArr2NumArr(tempObj[7]);
        //var smp = stringArr2NumArr(tempObj[8]);
        func.Init(domain, range, size, encode, decode, tempObj[4], tempObj[8], tempObj[9]);

    } else if (tempObj[0] === 2) {
        func = new PDExpIntFunc();
        var domain = stringArr2NumArr(tempObj[1]);
        var range = stringArr2NumArr(tempObj[2]);
        var c0 = stringArr2NumArr(tempObj[3]);
        var c1 = stringArr2NumArr(tempObj[4]);
        func.Init(domain, range, c0, c1, tempObj[5]);
    } else if (tempObj[0] === 3) {
        func = new PDStitchFunc();
        var domain = stringArr2NumArr(tempObj[1]);
        var range = stringArr2NumArr(tempObj[2]);

        var bound = stringArr2NumArr(tempObj[4]);
        var encode = stringArr2NumArr(tempObj[5]);
        func.Init(domain, range, tempObj[3], bound, encode);
    } else if (tempObj[0] === 4) {
        func = new PDPsFunc();
        var domain = stringArr2NumArr(tempObj[1]);
        var range = stringArr2NumArr(tempObj[2]);
        //var smp = stringArr2NumArr(tempObj[3]);
        func.Init(domain, range, tempObj[3]);
    }
    return func;
}  

function _LoadAFunc_InBin(binReader) {
    var func = null;
    var type = binReader.readUint8();
    var nCnt = 0;
    
    if (type === 0) {
        func = new PDSampledFunc();
        var size = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {size.push(binReader.readFloat32()); }
        var domain = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { domain.push(binReader.readFloat32()); }
        var range = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { range.push(binReader.readFloat32()); }
        var bitsPerSample = binReader.readUint8();
        var order = binReader.readUint8();
        var encode = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { encode.push(binReader.readFloat32()); }
        var decode = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { decode.push(binReader.readFloat32()); }
 //       var smpArr = [];
        nCnt = binReader.readInt32();
        var bytes = new Uint8Array(nCnt);
        for (var i = 0; i < nCnt; i++) { bytes[i] = binReader.readUint8(); }
        func.Init(domain, range, size, encode, decode, bitsPerSample, bytes, null);

    } else if (type === 2) {
        func = new PDExpIntFunc();
        var domain = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { domain.push(binReader.readFloat32()); }
        var range = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { range.push(binReader.readFloat32());}
        var c0 = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { c0.push(binReader.readFloat32());}
        var c1 = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) { c1.push(binReader.readFloat32());}
        var fExponent = binReader.readFloat32();
        func.Init(domain, range, c0, c1, fExponent);
    } else if (type === 3) {
        func = new PDStitchFunc();
        var domain = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {domain.push(binReader.readFloat32());}
        var range = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {range.push(binReader.readFloat32());}
        var bound = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {bound.push(binReader.readFloat32());}
        var encode = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {encode.push(binReader.readFloat32());}
        func.Init_InBin(domain, range, binReader, bound, encode);
    } else if (type === 4) {
        func = new PDPsFunc();
        var domain = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {domain.push(binReader.readFloat32());}
        var range = [];
        nCnt = binReader.readInt32();
        for (var i = 0; i < nCnt; i++) {range.push(binReader.readFloat32());}
        
        nCnt = binReader.readInt32();
        //for (var i = 0; i < nCnt; i++) { psStream.push(binReader.readInt8());}
        var stm = binReader.readString(nCnt);
        var arr = stm.split(",");
        func.Init(domain, range, arr);
    }
    return func;
}

/*
 *	Type0,3,4 Func
 */
function __LoadFuncs(jsonData){

    var arrFuncs = [];
    var nCount = jsonData.length;
    for (var i = 0; i < nCount; i++) {
        arrFuncs[i] = _LoadAFunc(jsonData[i]);
    }
        return arrFuncs;
}

function __LoadFuncs_InBin(binReader, nCount){
	var arrFuncs = [];
	for (var i = 0; i < nCount; i++) {
		arrFuncs[i] = _LoadAFunc_InBin(binReader);
	}
	return arrFuncs;
}

/*
 *	PDStitchFunc
 */
 function PDStitchFunc(){
    this.m_nInputs = 0;
    this.m_nOutputs = 0;
    this.m_Domains = [];
    this.m_Ranges = [];
    this.m_nSubs = 0;	// number of sub functions
	this.m_SubFunctions = [];
	this.m_Bounds = [];
//	this.pd_resource_Bounds = [];//Modeifyed by haipeng_yang 2013-08-14
	this.m_Encodes = [];
    this.CountOutputs = function(){return this.m_nOutputs;};
}

PDStitchFunc.prototype.Init = function (Domains/*array*/,Ranges/*array*/,jsonDataForArrFuncs, bounds, endcodes)
{
    this.m_nOutputs = 0;
    this.m_nInputs = 0;
    if (Domains != undefined && Domains !== null) {
        this.m_Domains = Domains;
        this.m_nInputs = Domains.length / 2;
    }

    if (Ranges != undefined && Ranges !== null) {
        this.m_Ranges = Ranges;
        this.m_nOutputs = Ranges.length / 2;
    }
    
    var old_outputs = this.m_nOutputs;
    // Type specific init
    //var ret = this.vInit();
	var ret = this.vInit(jsonDataForArrFuncs, bounds, endcodes); //Modeifyed by haipeng_yang 2013-08-14
	if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length > 0  && this.m_nOutputs > old_outputs)
    {
     //reset m_Ranges length and set zero to new element
        for(var i=0; i<(this.m_nOutputs - old_outputs) * 2; i++)
            this.m_Ranges[old_outputs * 2 + i] = 0;
    }
    return ret;
};

PDStitchFunc.prototype.Init_InBin = function (Domains/*array*/,Ranges/*array*/, binReader, bounds, endcodes) {
    this.m_nOutputs = 0;
    this.m_nInputs = 0;
    if (Domains != undefined && Domains !== null) {
        this.m_Domains = Domains;
        this.m_nInputs = Domains.length / 2;
    }

    if (Ranges != undefined && Ranges !== null) {
        this.m_Ranges = Ranges;
        this.m_nOutputs = Ranges.length / 2;
    }
    
    var old_outputs = this.m_nOutputs;
    // Type specific init
    //var ret = this.vInit();
	var ret = this.vInit_InBin(binReader, bounds, endcodes); //Modeifyed by haipeng_yang 2013-08-14
	if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length > 0  && this.m_nOutputs > old_outputs)
    {
     //reset m_Ranges length and set zero to new element
        for(var i=0; i<(this.m_nOutputs - old_outputs) * 2; i++)
            this.m_Ranges[old_outputs * 2 + i] = 0;
    }
    return ret;
};

PDStitchFunc.prototype.vInit = function (jsonDataForArrFuncs, arrBounds, arrEncodes) {
    this.m_SubFunctions = __LoadFuncs(jsonDataForArrFuncs);
    this.m_nSubs = this.m_SubFunctions.length;

    for (var i = 0; i < this.m_nSubs; i++) {
        var f = this.m_SubFunctions[i];
        //if(f.empty()) return false;
        if (!f) return false; //Modeifyed by haipeng_yang 2013-08-14

        if (f.CountOutputs() > this.m_nOutputs)
            this.m_nOutputs = f.CountOutputs();
    }

    // Load bounds
    this.m_Bounds[0] = this.m_Domains[0];
    for (var i = 0; i < this.m_nSubs; i++)
        this.m_Bounds[i + 1] = arrBounds[i];
    this.m_Bounds[this.m_nSubs] = this.m_Domains[1];

    // Load encoding
    for (var i = 0; i < this.m_nSubs * 2; i++)
        this.m_Encodes[i] = arrEncodes[i];

    return true;
};

PDStitchFunc.prototype.vInit_InBin = function (binReader, arrBounds, arrEncodes) {
	var nCount = binReader.readInt32();
    this.m_SubFunctions = __LoadFuncs_InBin(binReader, nCount);
    this.m_nSubs = this.m_SubFunctions.length;

    for (var i = 0; i < this.m_nSubs; i++) {
        var f = this.m_SubFunctions[i];
        //if(f.empty()) return false;
        if (!f) return false; //Modeifyed by haipeng_yang 2013-08-14

        if (f.CountOutputs() > this.m_nOutputs)
            this.m_nOutputs = f.CountOutputs();
    }

    // Load bounds
    this.m_Bounds[0] = this.m_Domains[0];
    for (var i = 0; i < this.m_nSubs; i++)
        this.m_Bounds[i + 1] = arrBounds[i];
    this.m_Bounds[this.m_nSubs] = this.m_Domains[1];

    // Load encoding
    for (var i = 0; i < this.m_nSubs * 2; i++)
        this.m_Encodes[i] = arrEncodes[i];

    return true;
};

PDStitchFunc.prototype.Call = function (inputs/*array*/, ninputs, results/*array*/) {

    if (this.m_nInputs != ninputs) return [false, 0];
	nresults = this.m_nOutputs;

	// First, clip all inputs to fit the domain
	for (var i = 0; i < this.m_nInputs; i ++) {
		if (inputs[i] < this.m_Domains[i*2])
			inputs[i] = this.m_Domains[i*2];
		else if (inputs[i] > this.m_Domains[i*2+1])
			inputs[i] = this.m_Domains[i*2]+1;
	}

    // Then, call the function
	this.vCall(inputs, results);

	// Finally, clip all outputs if ranges specified
	if (this.m_Ranges !== undefined && this.m_Ranges !== null && this.m_Ranges.length > 0) {
		for (var i = 0; i < this.m_nOutputs; i ++) {
			if (results[i] < this.m_Ranges[i*2])
				results[i] = this.m_Ranges[i*2];
			else if (results[i] > this.m_Ranges[i*2+1])
				results[i] = this.m_Ranges[i*2+1];
		}
	}

    return [true, nresults];//[/*bool Value for this function's result, nResults, */]
};

PDStitchFunc.prototype.vCall = function(inputs/*array*/,results/*array*/)
{
// Only have one input
    var input = inputs[0];

    // Determine the segment from bounds 
	var i;
	for (i = 0; i < this.m_nSubs-1; i ++){
	    if (input < this.m_Bounds[i + 1]) break;
	}
    
    if(this.m_SubFunctions[i] === null) return false;
    
    //input = PDF_Interpolate(input, m_Bounds[i], m_Bounds[i+1], m_Encodes[i*2], m_Encodes[i*2+1]);  
    input = PDF_Interpolate(input, this.m_Bounds[i], this.m_Bounds[i+1], this.m_Encodes[i * 2], this.m_Encodes[i * 2 + 1], this.m_Domains); //Modeifyed by haipeng_yang 2013-08-14
    
	// call corresponding function
    //var arrInputs = [1]; 
    var arrInputs = new Array();  //Modeifyed by haipeng_yang 2013-08-14
    
    arrInputs.push(input);
	var arr = this.m_SubFunctions[i].Call(arrInputs, this.m_nInputs, results);
	return arr[0];
};
//////////////////////////////////////////////////////////////////////////
function PDPsProc() {
    this.m_Operators = [];
    this.Parse = function (i, arrOpStm) {
        while (1) {
            if (i >= arrOpStm.length) return [false, i];
            var word = arrOpStm[i];
            i++;
            if (word == "}") return [true, i];


            if (word == "{") {
                var proc = new PDPsProc;
                this.m_Operators.push(42);
                this.m_Operators.push(proc);
                var r = proc.Parse(i, arrOpStm);
                if (r[0] == false) return [false, i];
                i = r[1];
            } else {
                var data = word; // word.shift();
                var reg = /^\d+$/;
               
                if (data.match(reg)) {
                    this.m_Operators.push(43);
                    this.m_Operators.push(data);
                } else if (data == "add") {
                    this.m_Operators.push(0);
                } else if (data == "sub") {
                    this.m_Operators.push(1);
                } else if (data == "mul") {
                    this.m_Operators.push(2);
                } else if (data == "div") {
                    this.m_Operators.push(3);
                } else if (data == "idiv") {
                    this.m_Operators.push(4);
                } else if (data == "mod") {
                    this.m_Operators.push(5);
                } else if (data == "neg") {
                    this.m_Operators.push(6);
                } else if (data == "abs") {
                    this.m_Operators.push(7);
                } else if (data == "ceiling") {
                    this.m_Operators.push(8);
                } else if (data == "foor") {
                    this.m_Operators.push(9);
                } else if (data == "round") {
                    this.m_Operators.push(10);
                } else if (data == "truncate") {
                    this.m_Operators.push(11);
                } else if (data == "sqrt") {
                    this.m_Operators.push(12);
                } else if (data == "sin") {
                    this.m_Operators.push(13);
                } else if (data == "cos") {
                    this.m_Operators.push(14);
                } else if (data == "atan") {
                    this.m_Operators.push(15);
                } else if (data == "exp") {
                    this.m_Operators.push(16);
                } else if (data == "ln") {
                    this.m_Operators.push(17);
                } else if (data == "log") {
                    this.m_Operators.push(18);
                } else if (data == "cvi") {
                    this.m_Operators.push(19);
                } else if (data == "cvr") {
                    this.m_Operators.push(20);
                } else if (data == "eq") {
                    this.m_Operators.push(21);
                } else if (data == "ne") {
                    this.m_Operators.push(22);
                } else if (data == "gt") {
                    this.m_Operators.push(23);
                } else if (data == "ge") {
                    this.m_Operators.push(24);
                } else if (data == "lt") {
                    this.m_Operators.push(25);
                } else if (data == "le") {
                    this.m_Operators.push(26);
                } else if (data == "and") {
                    this.m_Operators.push(27);
                } else if (data == "or") {
                    this.m_Operators.push(28);
                } else if (data == "xor") {
                    this.m_Operators.push(29);
                } else if (data == "not") {
                    this.m_Operators.push(30);
                } else if (data == "bitshift") {
                    this.m_Operators.push(31);
                } else if (data == "true") {
                    this.m_Operators.push(32);
                } else if (data == "false") {
                    this.m_Operators.push(33);
                } else if (data == "if") {
                    this.m_Operators.push(34);
                } else if (data == "ifelse") {
                    this.m_Operators.push(35);
                } else if (data == "pop") {
                    this.m_Operators.push(36);
                } else if (data == "exch") {
                    this.m_Operators.push(37);
                } else if (data == "dup") {
                    this.m_Operators.push(38);
                } else if (data == "copy") {
                    this.m_Operators.push(39);
                } else if (data == "index") {
                    this.m_Operators.push(40);
                } else if (data == "roll") {
                    this.m_Operators.push(41);
                }

                /*
                
                */
            }
        }



    };
    this.Execute = function (psEngine) {
        var size = this.m_Operators.length;
        for (var i = 0; i < size; i++) {
            var op = this.m_Operators[i];
            if (op == 42) {
                i++; // skip the procedure pointer
            } else if (op == 43) {
                psEngine.Push(this.m_Operators[i + 1]);
                i++;

            } else if (op == 34) {
                // the previous operator must be a procedure
                if (i < 2 || this.m_Operators[i - 2] != 42)
                    return false;
                if (psEngine.Pop()) {
                    var p = this.m_Operators[i - 1];
                    p.Execute(psEngine);
                }

            } else if (op == 35) {
                // the previous two operators must be a procedure
                if (i < 4 || this.m_Operators[i - 2] != 42 ||
					    this.m_Operators[i - 4] != 42)
                    return false;
                if (psEngine.Pop()) {
                    var p = this.m_Operators[i - 3];
                    p.Execute(psEngine);
                }
                else {
                    var p = this.m_Operators[i - 1];
                    p.Execute(psEngine);
                }
            } else {
                psEngine.DoOperator(op);
            }


        }//end for

    };
}

function PDPsEngine() {
    this.m_Stack = [];//size 100;
    this.m_StackCount = 0;
    this.m_MainProc = null;

    this.Parse = function (arrStm) {
     //   var word = arrStm[0];
     //   if(word != "{") return false;
		this.m_MainProc = new PDPsProc;
        var r = this.m_MainProc.Parse(0, arrStm);
        return r[0];
    };

    this.Execute = function () {
        return this.m_MainProc.Execute(this);
    };

    this.Reset = function () {
        this.m_StackCount = 0;
    };
    this.Push = function (v) {
        if (this.m_StackCount == 100) return;
        this.m_Stack[this.m_StackCount++] = v;
    };
    this.Pop = function () {
        if (this.m_StackCount == 0) return 0;
        return this.m_Stack[--this.m_StackCount];
    };

    this.DoOperator = function (op) {
        var i1, i2;
        var d1, d2;
        switch (op) {
            case 0:
                d1 = this.Pop();
                d2 = this.Pop();
                this.Push(d1 + d2);
                break;
            case 1:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push(d1 - d2);
                break;
            case 2:
                d1 = this.Pop();
                d2 = this.Pop();
                this.Push(d1 * d2);
                break;
            case 3:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push(d1 / d2);
                break;
            case 4:
                i2 = parseInt(this.Pop());
                i1 = parseInt(this.Pop());
			    Push(i1/i2);
                break;
            case 5:
                i2 = parseInt(this.Pop());
                i1 = parseInt(this.Pop());
                this.Push(i1 % i2);
                break;
            case 6:
                d1 = this.Pop();
                this.Push(-d1);
                break;
            case 7:
            d1 = this.Pop();
			this.Push(Math.abs(d1));
                break;
            case 8:
                d1 = this.Pop();
                this.Push(Math.ceil(d1));
                break;
            case 9:
                d1 = this.Pop();
                this.Push(Math.floor(d1));
                break;
            case 10:
                d1 = this.Pop();
                this.Push(Math.round(d1));
                break;
            case 11:
                i1 = parseInt(this.Pop());
                this.Push(i1);
                break;
            case 12:
                d1 = this.Pop();
                this.Push(Math.sqrt(d1));
                break;
            case 13:
                d1 = this.Pop();
                this.Push(Math.sin(d1 * Math.PI / 180.0));
                break;
            case 14:
                d1 = this.Pop();
                this.Push(Math.cos(d1 * Math.PI / 180.0));
                break;
            case 15:
                d2 = this.Pop();
                d1 = this.Pop();
			d1 = Math.atan2(d1, d2) * 180.0 / Math.PI;
			// PS use range from 0-360, atan2 returns -180 - 180
			if (d1 < 0) d1 += 360;
			this.Push(d1);
                break;
            case 16:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push(Math.pow(d1, d2));
                break;
            case 17:
                d1 = this.Pop();
            this.Push(Math.log(d1));
                break;
            case 18:
                d1 = this.Pop();
                this.Push(Math.log(d1) / Math.LN10);
                break;
            case 19:
                i1 = parseInt(this.Pop());
                this.Push(i1);
                break;
            case 20:
                break;
            case 21:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 == d2) ? 1 : 0);
                break;
            case 22:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 != d2) ? 1 : 0);
                break;
            case 23:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 > d2) ? 1 : 0);
                break;
            case 24:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 >= d2) ? 1 : 0);
                break;
            case 25:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 < d2) ? 1 : 0);
                break;
            case 26:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push((d1 <= d2) ? 1 : 0);
                break;
            case 27:
                i1 = parseInt(this.Pop());
                i2 = parseInt(this.Pop());
                this.Push(i1 & i2);
                break;
            case 28:
                i1 = parseInt(this.Pop());
                i2 = parseInt(this.Pop());
                this.Push(i1 | i2);
                break;
            case 29:
                i1 = parseInt(this.Pop());
                i2 = parseInt(this.Pop());
                this.Push(i1 ^ i2);
                break;
            case 30:
                i1 = parseInt(this.Pop());
                this.Push(parseInt(!i1));
                break;
            case 31:
                {
			var shift = parseInt(this.Pop());
			var i = parseInt(this.Pop());
			if (shift > 0)
			    this.Push(i << shift);
			else
			    this.Push(i >> -shift);
			break; }
            case 32:
                this.Push(1);
                break;
            case 33:
                this.Push(0);
                break;
            case 34:
            
                break;
            case 35:
                break;
            case 36:
                this.Pop();
                break;
            case 37:
                d2 = this.Pop();
                d1 = this.Pop();
                this.Push(d2);
                this.Push(d1);
                break;
            case 38:
                d1 = this.Pop();
                this.Push(d1);
                this.Push(d1);
                break;
            case 39:
                {
                    var n = parseInt(this.Pop());
                    if (n < 0 || n > 100 || this.m_StackCount + n > 100 || n > this.m_StackCount) break;
			for (var i = 0; i < n; i ++)
			    this.m_Stack[this.m_StackCount + i] = this.m_Stack[this.m_StackCount + i - n];
			this.m_StackCount += n;
			break; }
            case 40:
                {
                    var n = parseInt(this.Pop());
                    if (n < 0 || n >= this.m_StackCount) break;
                    this.Push(this.m_Stack[this.m_StackCount - n - 1]);
			break; }
            case 41:
                {
                    var j = parseInt(this.Pop());
                    var n = parseInt(this.Pop());
                    if (this.m_StackCount == 0) break;
                    if (n < 0 || n > this.m_StackCount) break;
			if (j < 0)
            {
				for (var i = 0; i < -j; i ++) {
				    var first = this.m_Stack[this.m_StackCount - n];
					for (var ii = 0; ii < n-1; ii ++)
					    this.m_Stack[this.m_StackCount - n + ii] = this.m_Stack[this.m_StackCount - n + ii + 1];
					this.m_Stack[this.m_StackCount - 1] = first;
				}
            }
			else
            {
				for (var i = 0; i < j; i ++) {
				    var last = this.m_Stack[this.m_StackCount - 1];
					var ii;
					for (ii = 0; ii < n-1; ii ++)
					    this.m_Stack[this.m_StackCount - ii - 1] = this.m_Stack[this.m_StackCount - ii - 2];
					this.m_Stack[this.m_StackCount - ii - 1] = last;
				}
             }
			break; }
            case 42:
                break;


            default:
        }
        return true;
    };
}

function PDPsFunc() {
    this.m_nInputs = 0;
    this.m_nOutputs = 0;
    this.m_Domains = [];
    this.m_Ranges = [];
    this.CountOutputs = function () { return this.m_nOutputs; };

    this.m_psEngine = null;
   // this.m_FuncStream = []; //ps function stream,such as [360,"mul","sin",2,"div","exch",360,"mul","sin",2,"div","add"];
}

PDPsFunc.prototype.Init = function (Domains/*array*/, Ranges/*array*/, arrFuncStream) {
    this.m_nOutputs = 0;
    this.m_nInputs = 0;
    if (Domains != undefined && Domains !== null) {
        this.m_Domains = Domains;
        this.m_nInputs = Domains.length / 2;
    }

    if (Ranges != undefined && Ranges !== null) {
        this.m_Ranges = Ranges;
        this.m_nOutputs = Ranges.length / 2;
    }

    var old_outputs = this.m_nOutputs;
    
    // Type specific init
    var ret = this.vInit(arrFuncStream);

    if (this.m_Ranges != undefined && this.m_Ranges !== null && this.m_Ranges.length >0 && this.m_nOutputs > old_outputs) {
        //reset m_Ranges length and set zero to new element
        for (var i = 0; i < (this.m_nOutputs - old_outputs) * 2; i++)
            this.m_Ranges[old_outputs * 2 + i] = 0;
    }
    return ret;
};

PDPsFunc.prototype.vInit = function (arrFuncStream) {
    //this.m_FuncStream = arrFuncStream;
    this.m_psEngine = new PDPsEngine;
    this.m_psEngine.Parse(arrFuncStream);
    return true;
};

PDPsFunc.prototype.vCall = function (inputs/*array*/, results/*array*/) {
   // var ps = new PDPsEngine;
    this.m_psEngine.Reset();
   // ps.Parse(this.m_FuncStream);

    // Push inputs into it
	for (var i = 0; i < this.m_nInputs; i ++)
	    this.m_psEngine.Push(inputs[i]);

    this.m_psEngine.Execute();

    if (this.m_psEngine.m_StackCount < this.m_nOutputs) return false;

    // Fill the outputs
    for (var i = 0; i < this.m_nOutputs; i++)
        results[this.m_nOutputs - i - 1] = this.m_psEngine.Pop();
};


PDPsFunc.prototype.Call = function (inputs/*array*/, ninputs, results/*array*/) {

    if (this.m_nInputs != ninputs) return [false, 0];
    nresults = this.m_nOutputs;

    // First, clip all inputs to fit the domain
    for (var i = 0; i < this.m_nInputs; i++) {
        if (inputs[i] < this.m_Domains[i * 2])
            inputs[i] = this.m_Domains[i * 2];
        else if (inputs[i] > this.m_Domains[i * 2 + 1])
            inputs[i] = this.m_Domains[i * 2] + 1;
    }

    // Then, call the function
    this.vCall(inputs, results);

    // Finally, clip all outputs if ranges specified
    if (this.m_Ranges !== null) {
        for (var i = 0; i < this.m_nOutputs; i++) {
            if (results[i] < this.m_Ranges[i * 2])
                results[i] = this.m_Ranges[i * 2];
            else if (results[i] > this.m_Ranges[i * 2 + 1])
                results[i] = this.m_Ranges[i * 2 + 1];
        }
    }

    return [true, nresults]; //[/*bool Value for this function's result, nResults, */]
};
